﻿/**
 * Peter
 * Created by: Peter Development Team
 *    http://peter.codeplex.com/
 * 
 * GNU General Public License version 2 (GPLv2)
 *    http://peter.codeplex.com/license
 *
 *  This code is provided on an AS IS basis, with no WARRANTIES,
 *  CONDITIONS or GUARANTEES of any kind.
 *
 **/

using System.Diagnostics;
using System.IO;
using System.Windows.Input;
using Peter.Common;
using Peter.Common.Controls;
using Peter.Common.Dialog;
using Peter.Common.Interfaces;
using Peter.Common.MainMenu;
using Peter.Common.Services;
using Peter.Common.Utilities;
using Peter.Data;
using Peter.Logic.Dialogs;
using Peter.Properties;

namespace Peter.Logic
{
   /// <summary>
   /// Model for the file explorer.
   /// </summary>
   public sealed class ModelFileExplorer : EditablePeterToolWindowWithMenu
   {
      private string m_SelectedFilePath;
      private ExplorerItem m_SelectedItem;
      private RelayCommand m_CmdRunProcess;
      private RelayCommand m_CmdProperties;
      private static RelayCommand s_FindFileCommand;

      /// <summary>
      /// Initializes a new file explorer model.
      /// </summary>
      public ModelFileExplorer ()
      {
         this.Title = "File Explorer";
         this.Icon = MainMenuIcon.Foldertree;
         this.ContentId = Guids.FILE_EXPLORER;
         this.ToolTip = "View the contents of your local hard drive";
         this.ContextMenuItems.Add (new PeterMainMenuItem
                                       {
                                          Title = "Edit in Peter",
                                          Icon = MainMenuIcon.Edit,
                                          Command = this.ExecuteCommand
                                       });
         this.ContextMenuItems.Add (new PeterMainMenuItem
                                       {
                                          Title = "Open with Windows",
                                          Icon = MainMenuIcon.Opennewwindow,
                                          Command = this.RunProcessCommand
                                       });
         this.ContextMenuItems.Add (new MainMenuSeperator ());
         this.ContextMenuItems.Add (new PeterMainMenuItem
                                       {
                                          Title = "Cut",
                                          Icon = MainMenuIcon.CutScissors,
                                          Command = this.EditCommand,
                                          CommandParameter = EditAction.Cut
                                       });
         this.ContextMenuItems.Add (new PeterMainMenuItem
                                       {
                                          Title = "Copy",
                                          Icon = MainMenuIcon.Copy,
                                          Command = this.EditCommand,
                                          CommandParameter = EditAction.Copy
                                       });
         this.ContextMenuItems.Add (new PeterMainMenuItem
                                       {
                                          Title = "Paste",
                                          Icon = MainMenuIcon.ClipboardPaste,
                                          Command = this.EditCommand,
                                          CommandParameter = EditAction.Paste
                                       });
         this.ContextMenuItems.Add (new PeterMainMenuItem
                                       {
                                          Title = "Delete",
                                          Icon = MainMenuIcon.Remove,
                                          Command = this.EditCommand,
                                          CommandParameter = EditAction.Delete
                                       });
         this.ContextMenuItems.Add (new MainMenuSeperator ());
         this.ContextMenuItems.Add (new PeterMainMenuItem
                                       {
                                          Title = "Properties",
                                          Icon = MainMenuIcon.Reliability,
                                          Command = this.PropertiesCommand
                                       });
         Instance = this;
      }

      /// <summary>
      /// The current instance of the file explorer.
      /// </summary>
      private static ModelFileExplorer Instance { get; set; }

      /// <summary>
      /// Gets the run process command.
      /// </summary>
      public ICommand RunProcessCommand
      {
         get
         {
            return this.m_CmdRunProcess ?? (this.m_CmdRunProcess = new RelayCommand (
               delegate { Process.Start (this.SelectedItem.File.FullName); },
               delegate { return this.SelectedItem != null; }));
         }
      }

      /// <summary>
      /// Gets the properties command.
      /// </summary>
      public ICommand PropertiesCommand
      {
         get
         {
            return this.m_CmdProperties ?? (this.m_CmdProperties = new RelayCommand (
               delegate { CommonUtilities.ShowFileProperties (this.SelectedItem.File.FullName); },
               delegate { return this.SelectedItem != null; }));
         }
      }

      /// <summary>
      /// Gets or Sets the selected item.
      /// </summary>
      public ExplorerItem SelectedItem
      {
         get { return this.m_SelectedItem; }
         set
         {
            this.m_SelectedItem = value;
            this.RaisePropertyChanged ("SelectedItem");
            this.UpdateMenuContext (this.SelectedItem);
         }
      }

      /// <summary>
      /// Checks whether an edit actions can be performed or not.
      /// </summary>
      /// <param name="obj">Edit action</param>
      /// <returns>True if action can be performed, otherwise false.</returns>
      protected override bool CanEdit (object obj)
      {
         if (this.SelectedItem == null || obj == null) return false;
         var editAction = (EditAction)obj;
         switch (editAction)
         {
            case EditAction.Paste:
               return this.SelectedItem.IsDirectory && ClipboardFiles.CanPaste ();
            case EditAction.Copy:
            case EditAction.Cut:
               return this.SelectedItem.Parent != null && !this.SelectedItem.IsDirectory;
            case EditAction.Delete:
               return this.SelectedItem.Parent != null;
            default:
               return false;
         }
      }

      /// <summary>
      /// Performs the edit action.
      /// </summary>
      /// <param name="obj">Edit action.</param>
      protected override void OnEdit (object obj)
      {
         var editAction = (EditAction)obj;
         switch (editAction)
         {
            case EditAction.Copy:
               ClipboardFiles.Copy (new [] { this.SelectedItem.File.FullName });
               break;
            case EditAction.Cut:
               ClipboardFiles.Cut (new[] { this.SelectedItem.File.FullName });
               this.SelectedItem.Parent.Children.Remove (this.SelectedItem);
               break;
            case EditAction.Paste:
               var pastedFiles = ClipboardFiles.Paste (this.SelectedItem.File.FullName);
               foreach (var pastedFile in pastedFiles)
               {
                  this.SelectedItem.Children.Add (new ExplorerItem (this.SelectedItem, new FileInfo (pastedFile)));
               }
               break;
            case EditAction.Delete:
               var message = new ModelMessageDialog
                                {
                                   Title = Resources.Delete,
                                   Tag = this.SelectedItem,
                                   Icon = MainMenuIcon.QuestionSign,
                                   ShowCancelButton = false,
                                   ShowOkButton = false,
                                   ShowNoButton = true,
                                   ShowYesButton = true,
                                   SubMessage = this.SelectedItem.File.FullName,
                                   Message = this.SelectedItem.File is DirectoryInfo ? Resources.DeleteDirectory : Resources.DeleteFile
                                };
               message.Closing += this.OnDeleteClosing;
               this.Peter.ShowDialog (message);
               break;
         }
      }

      /// <summary>
      /// Occurs when the delete message is closing.
      /// </summary>
      /// <param name="dialog"></param>
      private void OnDeleteClosing (DialogBase dialog)
      {
         dialog.Closing -= this.OnDeleteClosing;
         if (dialog.DialogCloseAction == DialogCloseAction.Yes)
         {
            var fileItem = dialog.Tag as ExplorerItem;
            if (fileItem != null)
            {
               var file = fileItem.File as FileInfo;
               if (file != null)
               {
                  file.Delete ();
                  fileItem.Parent.Children.Remove (fileItem);
               }
               else
               {
                  var dir = fileItem.File as DirectoryInfo;
                  if (dir != null)
                  {
                     dir.Delete (true);
                     fileItem.Parent.Children.Remove (fileItem);
                  }
               }
            }
         }
      }

      /// <summary>
      /// Gets the find file command.
      /// </summary>
      public static ICommand FindFileCommand
      {
         get { return s_FindFileCommand ?? (s_FindFileCommand = new RelayCommand (OnFindFile, CanFindFile)); }
      }

      /// <summary>
      /// Gets or Sets the selected path of the file.
      /// </summary>
      public string SelectedFilePath
      {
         get { return m_SelectedFilePath; }
         set
         {
            this.m_SelectedFilePath = value;
            this.RaisePropertyChanged ("SelectedFilePath");
         }
      }

      /// <summary>
      /// Checks to see if the find file command can execute.
      /// </summary>
      /// <param name="obj">The full path to the file.</param>
      /// <returns>True if the file exists, otherwise false.</returns>
      private static bool CanFindFile (object obj)
      {
         return obj != null && File.Exists (obj.ToString ());
      }

      /// <summary>
      /// Finds the given file in the explorer.
      /// </summary>
      /// <param name="obj">The full path to the file.</param>
      private static void OnFindFile (object obj)
      {
         if (Instance == null)
         {
            var peter = ServiceManager.Get <IPeter> ();
            peter.ShowToolWindow (PeterToolWindows.FileExplorer);
         }

         if (Instance != null)
         {
            Instance.Peter.ShowToolWindow (PeterToolWindows.FileExplorer);
            Instance.SelectedFilePath = obj.ToString ();
         }
      }

      /// <summary>
      /// Checks to see if the selected item can execute.
      /// </summary>
      /// <param name="obj">Command parameter.</param>
      /// <returns>True if command can execute, other wise false.</returns>
      protected override bool CanExecute (object obj)
      {
         var file = obj as FileSystemInfo;
         if (file != null)
            return file is FileInfo;

         if (this.SelectedItem != null)
            return !this.SelectedItem.IsDirectory;

         return false;
      }

      /// <summary>
      /// Executes the execute command.
      /// </summary>
      /// <param name="obj">Command Parameter.</param>
      protected override void OnExecute (object obj)
      {
         var file = obj as FileInfo;
         if (file != null)
         {
            this.Peter.OpenFile (file.FullName);
         }

         if (this.SelectedItem != null)
            this.Peter.OpenFile (this.SelectedItem.File.FullName);
      }
   }
}
